package picounit.java.type;

import static picounit.java.collection.CollectionUtil.*;
import picounit.exception.ClassNotFound;
import picounit.java.collection.Filter;
import picounit.java.collection.Iterable;
import picounit.java.collection.List;
import picounit.java.file.ClassFile;
import picounit.loader.Instantiator;

public class Type {
    public Type(String className) {
        try {
            this.clazz = Class.forName(className);
        }
        catch (ClassNotFoundException classNotFoundException) {
            throw new ClassNotFound(className);
        }
    }

    public Type(Class<?> clazz) {
        this.clazz = clazz;
    }

    public Iterable<Type> siblings() {
        return new ClassFile(clazz).siblings();
    }

    public boolean isAssignableFrom(Type type) {
        return type.superTypes().passes(new Filter<Type>() {
            public boolean passes(Type type) {
                return type.hasClass(clazz);
            }
        });
    }

    public Iterable<Method> publicMethods() {
        return List.create(clazz.getDeclaredMethods())
            .convert(Method.FACTORY)
            .filter(negate(Method.STATIC_FILTER));
    }

    public Iterable<Type> superTypes() {
        return new SuperTypes(clazz);
    }

    public boolean hasClass(Class<?> clazz) {
        return this.clazz.equals(clazz);
    }

    public Object newInstance() {
        return new Instantiator().instantiate(clazz);
    }

    @Override
    public boolean equals(Object object) {
        if (object == null || !object.getClass().equals(getClass())) {
            return false;
        }

        Type other = (Type) object;

        return other.hasClass(clazz);
    }

    @Override
    public int hashCode() {
        return clazz.hashCode();
    }

    @Override
    public String toString() {
        return String.format("Type: %s", clazz.getName());
    }

    private final Class<?> clazz;
}